package touchtrack.view.graph;

import org.mt4j.MTApplication;
import org.mt4j.components.MTComponent;

import processing.core.PApplet;
import processing.core.PFont;
import processing.core.PImage;
import touchtrack.Settings;
import touchtrack.model.track.LocalTrack;
import touchtrack.model.track.TrackInterface;
import traer.physics.Particle;
import traer.physics.ParticleSystem;

/**
 * TrackNode
 * 
 * A node in a graph, representing a Track.
 * 
 * @author	MuMe09 team 6
 * 			http://mumetech.wordpress.com/
 */
public class TrackNode extends MTComponent
{
	MTApplication app;
	private PFont font;
	private TrackInterface track;
	public Particle particle;
	public boolean selected = false;
	
	/**
	 * TrackNode constructor.
	 * 
	 * @param 	MTApplication app
	 * @param 	TrackInterface track
	 */
	public TrackNode(MTApplication app, TrackInterface track)
	{
		super(app);
		this.app = app;
		this.font = app.loadFont(Settings.FONT);
		this.track = track;
		
		// (used for efficiency)
		this.setWidth();
		this.setHeight();
	}
	
	/***************************************************************************
	 * 								Drawing
	 ***************************************************************************/
	
	/**
	 * Draws the node.
	 */
	public void draw(TrackGraph graph)
	{
		if(graph != null && track != null && track.getImage() != null && particle != null)
		{
	        int x = (int) (PApplet.map(getX(), graph.getMinX(), graph.getMaxX(), graph.BORDER, app.getWidth() - graph.BORDER));
	        int y = (int) (PApplet.map(getY(), graph.getMinY(), graph.getMaxY(), graph.BORDER, app.getHeight() - graph.BORDER));
	        
	        app.smooth();
	        app.strokeWeight(2);
	        app.textFont(font);
	        app.rectMode(PApplet.CENTER);
			app.imageMode(PApplet.CENTER);
			app.textAlign(PApplet.CENTER);
			
	        // local or external track?
	        if (track instanceof LocalTrack)
	        {
		        if (this.selected)
		        {
		        	app.fill(94, 241, 229); // cyan
		        }
		        else
		        {
		        	app.fill(63, 154, 211); // blueish
		        }
		        
		        app.rect(x, y, this.getWidth() + 16, this.getHeight() + 16);
	        }
	        else
	        {
		        if (this.selected)
		        {
		        	app.fill(255, 255, 0); // yellow
			        app.rect(x, y, this.getWidth() + 16, this.getHeight() + 16);
		        }
		        else
		        {
		        	app.fill(255); // white
		        }
	        }
	        
			//PImage image = new PImage(track.getImage());
			PImage image = Settings.toPImage(track.getImage());
			app.image(image, x, y, this.getWidth(), this.getHeight());
			app.text(track.toString(), x, y + this.getHeight() * 0.5f + 20);
	        
	        // Selected?
	        if (this.selected)
	        {
	        	particle.makeFixed();
	        	
	        	// more info button
	        	PImage infoButton = app.loadImage("moreinfobutton.png");
	        	app.image(infoButton, x, y - this.getHeight() * 0.5f - 28, this.getWidth() + 16, 20);
	        	
	        	// remove button
	        	PImage removeButton = app.loadImage("removebutton.png");
	        	app.image(removeButton, x, y - this.getHeight() * 0.5f - 48, this.getWidth() + 16, 20);
	        }
	        else
	        {
	        	particle.makeFree();
	        }
		}
	}
	
	/**
	 * @param		TrackGraph
     * @param       otherX (center)
     * @param       otherY (center)
     * @return      True if the given point lays in this node (or its border), else false.
     */
    public boolean inImage(TrackGraph graph, float otherX, float otherY)
    {
        int x = (int) (PApplet.map(getX(), graph.getMinX(), graph.getMaxX(), graph.BORDER, app.getWidth() - graph.BORDER));
        int y = (int) (PApplet.map(getY(), graph.getMinY(), graph.getMaxY(), graph.BORDER, app.getHeight() - graph.BORDER));
        
        // map to screen
        x = (int) app.screenX(x, y);
        y = (int) app.screenY(x, y);
        
	    float left = (x - getWidth() * 0.5f);
	    float top = (y - getHeight() * 0.5f);
	    float right = (x + getWidth() * 0.5f);
	    float bottom = (y + getHeight() * 0.5f);
	    
	    //System.out.println("[DEBUG] Checking if (" + otherX + ", " + otherY + ") lies in (" + left + ", " + right + ") and (" + right + ", " + bottom + ")...");
	
	    if (otherX > left && otherX < right)
	            if (otherY > top && otherY < bottom)
	                    return true;
	    
	    return false;
    }
	
	/**
	 * @param		TrackGraph
     * @param       otherX (center)
     * @param       otherY (center)
     * @return      True if the given point lays in the "more info" button (or its border), else false.
     */
    public boolean inInfoButton(TrackGraph graph, float otherX, float otherY)
    {
        int x = (int) (PApplet.map(getX(), graph.getMinX(), graph.getMaxX(), graph.BORDER, app.getWidth() - graph.BORDER));
        int y = (int) (PApplet.map(getY(), graph.getMinY(), graph.getMaxY(), graph.BORDER, app.getHeight() - graph.BORDER));
        y -= this.getHeight() * 0.5f + 38; 
        
        // map to screen
        x = (int) app.screenX(x, y);
        y = (int) app.screenY(x, y);
        
	    float left = (x - 45);
	    float top = (y - 14);
	    float right = (x + 45);
	    float bottom = (y + 14);
	    
	    //System.out.println("[DEBUG] Checking if (" + otherX + ", " + otherY + ") lies in (" + left + ", " + right + ") and (" + right + ","+top+" " + bottom + ")...");
	
	    if (otherX > left && otherX < right)
	            if (otherY > top && otherY < bottom)
	                    return true;
	    
	    return false;
    }
	
	/**
	 * @param		TrackGraph
     * @param       otherX (center)
     * @param       otherY (center)
     * @return      True if the given point lays in the "remove" button (or its border), else false.
     */
    public boolean inRemoveButton(TrackGraph graph, float otherX, float otherY)
    {
        int x = (int) (PApplet.map(getX(), graph.getMinX(), graph.getMaxX(), graph.BORDER, app.getWidth() - graph.BORDER));
        int y = (int) (PApplet.map(getY(), graph.getMinY(), graph.getMaxY(), graph.BORDER, app.getHeight() - graph.BORDER));
        y -= this.getHeight() * 0.5f + 48; 
        
        // map to screen
        x = (int) app.screenX(x, y);
        y = (int) app.screenY(x, y);
        
	    float left = (x - 45);
	    float top = (y - 14);
	    float right = (x + 45);
	    float bottom = (y + 14);
	    
	    //System.out.println("[DEBUG] Checking if (" + otherX + ", " + otherY + ") lies in (" + left + ", " + right + ") and (" + right + ","+top+" " + bottom + ")...");
	
	    if (otherX > left && otherX < right)
	            if (otherY > top && otherY < bottom)
	                    return true;
	    
	    return false;
    }
	
	/***************************************************************************
	 * 							Node properties
	 ***************************************************************************/
	
	/**
	 * @return	TrackInterface
	 */
	public TrackInterface getTrackInterface()
	{
		return this.track;
	}
	
	/**
	 * Sets width variable.
	 */
	private void setWidth()
	{
		this.width = 80;
	}
	
	/**
	 * @return	float width
	 */
	public float getWidth()
	{
		return width;
	}
	
	/**
	 * Sets height variable.
	 */
	private void setHeight()
	{
		int imageWidth = track.getImage().getWidth(null);
		int imageHeight = track.getImage().getHeight(null);
		
		float f = getWidth() / imageWidth;
		
		this.height = imageHeight * f;
	}
	
	/**
	 * @return	float height
	 */
	public float getHeight()
	{
		return height;
	}
	
	/**
	 * @return	float x
	 */
	public float getX()
	{
		return particle.position().x(); 
	}
	
	/**
	 * @return	float y
	 */
	public float getY()
	{
		return particle.position().y();
	}
	
	// not necessary, but should improve speed somewhat
	private float width;
	private float height;
	
	/***************************************************************************
	 * 							Particle properties
	 ***************************************************************************/

    /**
     * Removes a Particle from the system.
     * 
     * @param 	ParticleSystem physics
     */
    public void killParticle(ParticleSystem physics) 
    {
    	physics.removeParticle(particle);
    }

    /**
     * @param 	float x1
     * @param 	float y1
     */
    public void moveParticle(float x1, float y1)
    {
        //this.particle.moveTo(x1, y1, 0.0F);
    	this.particle.velocity().set(x1, y1, 0.0F);
    }
	
	/***************************************************************************
	 * 							Distance to others
	 ***************************************************************************/
	
	/**
	 * Returns the distance to a given other track node.
	 * 
	 * @param 	TrackNode otherTrack
	 * @return	distance in graph as float
	 */
	public float getDistance(TrackNode otherTrack)
	{
		float dx = getDistanceX(otherTrack); 
	    float dy = getDistanceY(otherTrack); 
	    return (float) Math.sqrt(dx*dx+dy*dy);
	}
	
	/**
	 * Returns the distance over the x direction to a given other track node.
	 * 
	 * @param 	otherTrack
	 * @return	float |delta x|
	 */
	public float getDistanceX(TrackNode otherTrack)
	{
		return Math.abs(otherTrack.getX() - this.getX());
	}
	
	/**
	 * Returns the distance over the y direction to a given other track node.
	 * 
	 * @param 	otherTrack
	 * @return	float |delta y|
	 */
	public float getDistanceY(TrackNode otherTrack)
	{
		return Math.abs(otherTrack.getY() - this.getY());
	}
	
	/***************************************************************************
	 * 								Removal
	 ***************************************************************************/
	
	/**
	 * @param	ParticleSystem physics
	 */
	public void remove(ParticleSystem physics)
	{
		this.killParticle(physics);
	}
	
	/***************************************************************************
	 * 								Java stuff
	 ***************************************************************************/
	
	/**
	 * @return	String
	 */
	public String toString()
	{
		return track.toString();
	}
	
	/**
	 * @param	Object
	 * @return	boolean
	 */
	public boolean equals(Object obj)
	{
		if (obj instanceof TrackNode)
		{
			TrackNode t = (TrackNode) obj;
			return track.equals(t.track);
		}
		else
		{
			return false;
		}
	}
}